package com.cmc.cloud.cmclink.doc.service.impl.si;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmc.cloud.cmclink.bdt.api.base.FinanceItemApi;
import com.cmc.cloud.cmclink.bdt.api.base.dto.req.FinanceItemListByCodeReqVO;
import com.cmc.cloud.cmclink.bdt.api.base.dto.resp.FinanceItemRespDTO;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoLocalCostImErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.BlnoCntrConvert;
import com.cmc.cloud.cmclink.doc.convert.BlnoFreightConvert;
import com.cmc.cloud.cmclink.doc.convert.BlnoLocalCostExConvert;
import com.cmc.cloud.cmclink.doc.convert.BlnoLocalCostImConvert;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.rpcservice.AccountTransfer;
import com.cmc.cloud.cmclink.doc.rpcservice.CustomerRpcService;
import com.cmc.cloud.cmclink.doc.service.BlnoCntrService;
import com.cmc.cloud.cmclink.doc.service.si.*;
import com.cmc.cloud.cmclink.doc.util.VslvoyUtil;
import com.cmc.cloud.cmclink.doc.vo.blnocntrvo.BlnoCntrRespVO;
import com.cmc.cloud.cmclink.doc.vo.blnolocalcostdetailvo.BlnoLocalCostDetailAllRespVO;
import com.cmc.cloud.cmclink.doc.vo.blnolocalcostdetailvo.BlnoLocalCostExDetailRespVO;
import com.cmc.cloud.cmclink.doc.vo.blnolocalcostvo.*;
import com.cmc.cloud.cmclink.fin.api.base.dto.req.DocBlnoBaseReqDTO;
import com.cmc.cloud.cmclink.fin.api.base.dto.req.DocBlnoCreateReqDTO;
import com.cmc.cloud.cmclink.fin.api.base.dto.req.DocBlnoFreightInfoReqDTO;
import com.cmc.cloud.cmclink.fin.api.base.dto.req.DocBlnoVslvoyReqDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cmes.framework.security.core.util.SecurityFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 出口提单本地费用 Service 实现类
 *
 * @author 招商轮船-管理员
 */
@Service
@Validated
@Slf4j
public class BlnoLocalCostExServiceImpl implements BlnoLocalCostExService {
    @Resource
    private BlnoMapper blnoMapper;
    @Resource
    private BlnoCntrService blnoCntrService;

    @Resource
    private BlnoCargoService blnoCargoService;

    @Resource
    private BlnoVslvoyService blnoVslvoyService;

    @Resource
    private BlnoLocalCostMapper blnoLocalCostMapper;

    @Resource
    private BlnoLocalCostDetailMapper blnoLocalCostDetailMapper;

    @Resource
    private CalculateOperateLogService calLogService;

    @Resource
    private AccountTransfer accountTransfer;

    @Resource
    private CustomerRpcService nameTransferService;

    @Resource
    private FinanceItemApi financeItemApi;

    @Override
    public List<BlnoLocaCoastExRespVO> getBlnoWithLocalCostList(BlnoLocalCostExReqVO reqVO) {
        List<String> rejectStatuList = new ArrayList<>();
        rejectStatuList.add(SiStatusEnum.BookingDraft.getValue());
        rejectStatuList.add(SiStatusEnum.BookingProcessing.getValue());

        //do_status	D/O Status	D/O状态	查看基础数据D/O状态
        //pod_bl_flag		目的港提单标记 true //todo 待确认

        reqVO.setRejectStatuList(rejectStatuList);

        List<BlnoLocaCoastExRespVO> respList = blnoMapper.getBlnoWithLocalCostList(reqVO);
        if (CollectionUtils.isEmpty(respList)) {
            return new ArrayList<>();
        }
        Set<Long> ids = respList.stream().map(BlnoLocaCoastExRespVO::getBlnoId).collect(Collectors.toSet());
        List<BlnoCargoDO> blnoCargoDOList = blnoCargoService.getBlnoCargoByBlnoIds(ids);
        Map<Long, List<BlnoCargoDO>> cargoListMap =
            blnoCargoDOList.stream().collect(Collectors.groupingBy(BlnoCargoDO::getBlnoId));

        // 获取供应商信息
        Set<String> codes = respList.stream().map(BlnoLocaCoastExRespVO::getBkpCode).filter(Objects::nonNull)
            .filter(name -> !name.isEmpty()).collect(Collectors.toSet());
        Map<String, String> companyNameMap = nameTransferService.getCompanyNameMap(new ArrayList<>(codes));

        respList.forEach(resp->{
            resp.setToAccountFlag(resp.getToAccount() != null && resp.getToAccount() != 0);
            resp.setBkpNameEn(companyNameMap.getOrDefault(resp.getBkpCode(), null));
            List<BlnoCargoDO> cargoTmpList = cargoListMap.get(resp.getBlnoId());
            if (!CollectionUtils.isEmpty(cargoTmpList)) {
                resp.setCargoType(cargoTmpList.get(0).getCargoType());//join
            }
        });
        return respList;
    }


    @Override
    public void deleteExDetailByBlnoIdList(Collection<Long> blnoIds) {
        LambdaQueryWrapperX<BlnoLocalCostDetailDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(BlnoLocalCostDetailDO::getBlnoId, blnoIds).eq(BlnoLocalCostDetailDO::getImExType,
            ExImTypeEnum.EX.getValue());
        blnoLocalCostDetailMapper.delete(queryWrapperX);
    }
    @Override
    public BlnoLocalCostDetailAllRespVO getBlnoAllLocalCost(Long id) {
        List<BlnoLocalCostDO> localCostList = getLocalCostByBlnoId(id, ExImTypeEnum.EX.getValue());
        BlnoDO blno = blnoMapper.selectById(id);

        List<BlnoCntrDO> blnoCntrDOList = blnoCntrService.getBlnoCntrByBlnoId(id);
        List<BlnoCntrRespVO> blnoCntrRespVos = BlnoCntrConvert.INSTANCE.convertList(blnoCntrDOList);
        blnoCntrRespVos.forEach(cntr -> cntr.setCntrStatus(blno.getCntrStatus()));

        CalculateOperateLogDO logDO =
            calLogService.getNewOperateLogByBlnoId(id, CalculateTypeEnum.CALCULATE_LOCAL_COST_EX);

        // 获取供应商信息
        List<BlnoLocalCostExDetailRespVO> costRespVOList = BlnoLocalCostExConvert.INSTANCE.convertList(localCostList);
        // 过滤null
        Set<String> codes = costRespVOList.stream().map(BlnoLocalCostExDetailRespVO::getVendor).filter(Objects::nonNull)
                .filter(name -> !name.isEmpty()).collect(Collectors.toSet());
        Map<String, String> companyNameMap = nameTransferService.getCompanyNameMap(new ArrayList<>(codes));
        costRespVOList.forEach(cost -> cost.setVendorNameEn(companyNameMap.getOrDefault(cost.getVendor(), null)));

        BlnoLocalCostDetailAllRespVO respVO = new BlnoLocalCostDetailAllRespVO();
        respVO.setLocalCost(costRespVOList);
        respVO.setContainerInfo(blnoCntrRespVos);
        if (logDO != null) {
            respVO.setSendJson(logDO.getSendJson());
            respVO.setReceiveJson(logDO.getReceiveJson());
        }
        return respVO;
    }

    @Override
    public List<BlnoLocalCostDO> getLocalCostByBlnoId(Long blnoId, String exImType) {
        LambdaQueryWrapperX<BlnoLocalCostDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(BlnoLocalCostDO::getBlnoId, blnoId).eqIfPresent(BlnoLocalCostDO::getImExType, exImType);
        queryWrapperX.orderByAsc(BlnoLocalCostDO::getId);
        return blnoLocalCostMapper.selectList(queryWrapperX);
    }

    @Override
    public void createBlnoLocalCost(BlnoLocalCostExCreateReqVO createReqVO) {
        BlnoLocalCostDO blnoLocalCost = BlnoLocalCostExConvert.INSTANCE.convert(createReqVO);
        blnoLocalCost.setRecordType(RecordTypeEnum.MANUAL.getValue());
        blnoLocalCost.setOriginalId(null);
        blnoLocalCost.setToAccount(false);
        blnoLocalCost.setPayment(PaymentEnum.P.getValue());
        blnoLocalCost.setImExType(ExImTypeEnum.EX.getValue());
        blnoLocalCostMapper.insert(blnoLocalCost);
    }

    @Override
    public void updateBlnoLocalCost(BlnoLocalCostExUpdateReqVO updateReqVO) {
        BlnoLocalCostDO blnoLocalCostDO = BlnoLocalCostExConvert.INSTANCE.convert(updateReqVO);
        validOperateAllow(updateReqVO.getId());
        blnoLocalCostDO.setRecordType(RecordTypeEnum.MANUAL.getValue());
        blnoLocalCostDO.setOriginalId(null);
        blnoLocalCostDO.setToAccount(false);
        blnoLocalCostDO.setPayment(PaymentEnum.P.getValue());
        blnoLocalCostDO.setImExType(ExImTypeEnum.EX.getValue());
        blnoLocalCostMapper.updateById(blnoLocalCostDO);
    }

    private void validOperateAllow(Long id) {
        BlnoLocalCostDO blnoLocalCostDO = blnoLocalCostMapper.selectById(id);
        if (blnoLocalCostDO == null) {
            throw ServiceExceptionUtil.exception(BlnoLocalCostImErrorCodeConstants.BLNO_LOCAL_COST_NOT_EXISTS);
        }

        if (RecordTypeEnum.AUTO.getValue().equals(blnoLocalCostDO.getRecordType()) || blnoLocalCostDO.getToAccount()) {
            throw ServiceExceptionUtil.exception(BlnoLocalCostImErrorCodeConstants.LOCAL_COST_IM_OPERATE_NOT_ALLOWED);
        }
    }

    @Override
    public void deleteBlnoLocalCost(Long id) {
        validOperateAllow(id);
        blnoLocalCostMapper.deleteById(id);
    }



    private DocBlnoBaseReqDTO buildDocBlnoBaseReqDTO(List<BlnoLocalCostDO> blnoLocalCostDOList, List<BlnoDO> blnoDOList,
        List<BlnoVslvoyDO> blnoVslvoyDOList) {
        Map<Long, List<BlnoVslvoyDO>> vslvoyListMap =
            blnoVslvoyDOList.stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
        Map<Long, List<BlnoLocalCostDO>> localCostListMap =
            blnoLocalCostDOList.stream().collect(Collectors.groupingBy(BlnoLocalCostDO::getBlnoId));
        DocBlnoBaseReqDTO docBlnoBaseReq = new DocBlnoBaseReqDTO();
        List<DocBlnoCreateReqDTO> docBlnoList = new ArrayList<>();
        blnoDOList.forEach(blnoDO -> {
            DocBlnoCreateReqDTO blnoReq = BlnoFreightConvert.INSTANCE.convertAccountReq(blnoDO);
            blnoReq.setSender(String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String formattedString = now.format(formatter);
            blnoReq.setSendTime(formattedString);
            List<DocBlnoVslvoyReqDTO> docBlnoVslvoyList =
                BlnoFreightServiceImpl.buildVslvoyReq(vslvoyListMap.getOrDefault(blnoDO.getId(), new ArrayList<>()));
            String localPort = VslvoyUtil.getLastPort(vslvoyListMap.getOrDefault(blnoDO.getId(), new ArrayList<>()));
            List<DocBlnoFreightInfoReqDTO> freightInfoList =
                buildFreightReq(localCostListMap.getOrDefault(blnoDO.getId(), new ArrayList<>()), localPort);
            blnoReq.setDocBlnoVslvoyList(docBlnoVslvoyList);
            blnoReq.setFreightInfoList(freightInfoList);
            docBlnoList.add(blnoReq);
        });
        docBlnoBaseReq.setDocBlnoList(docBlnoList);
        return docBlnoBaseReq;
    }

    private List<DocBlnoFreightInfoReqDTO> buildFreightReq(List<BlnoLocalCostDO> blnoLocalCostList, String localPort) {
        List<DocBlnoFreightInfoReqDTO> vslvoyReqList = new ArrayList<>();
        blnoLocalCostList.forEach(freight -> {
            DocBlnoFreightInfoReqDTO freightReq = BlnoLocalCostImConvert.INSTANCE.convertLocalCostReq(freight);
            freightReq.setLocalPortCode(localPort);
            vslvoyReqList.add(freightReq);
        });
        return vslvoyReqList;
    }

    @Override
    public List<BlnoLocalCostUpdateReqVO> getBlnoCostByBlnoId(Long blnoId) {
        LambdaQueryWrapper<BlnoLocalCostDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BlnoLocalCostDO::getBlnoId, blnoId);
        queryWrapper.orderByAsc(BlnoLocalCostDO::getId);
        List<BlnoLocalCostDO> blnoLocalCostDos = blnoLocalCostMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(blnoLocalCostDos)) {
            List<String> codes = blnoLocalCostDos.stream().map(BlnoLocalCostDO::getChargeCode).collect(Collectors.toList());
            List<BlnoLocalCostUpdateReqVO> blnoLocalCostExUpdateReqVos = BeanUtil.copyToList(blnoLocalCostDos, BlnoLocalCostUpdateReqVO.class);
            List<FinanceItemRespDTO> checkedData = null;
            try {
                FinanceItemListByCodeReqVO financeItemListByCodeReqVO = new FinanceItemListByCodeReqVO(codes);
                checkedData = financeItemApi.listByCode(financeItemListByCodeReqVO).getCheckedData();
            } catch (Exception e) {
                throw ServiceExceptionUtil.exception(BlnoLocalCostImErrorCodeConstants.BLNO_FINANCE_ERROR);
            }
            if(CollectionUtil.isNotEmpty(checkedData)){
                Map<String, String> cnMap = checkedData.stream().collect(Collectors.toMap(FinanceItemRespDTO::getCode, FinanceItemRespDTO::getItemNameCn));
                Map<String, String> enMap = checkedData.stream().collect(Collectors.toMap(FinanceItemRespDTO::getCode, FinanceItemRespDTO::getItemNameEn));

                blnoLocalCostExUpdateReqVos.forEach(n -> {
                    n.setChargeNameCn(cnMap.get(n.getChargeCode()));
                    n.setChargeNameEn(enMap.get(n.getChargeCode()));
                });
            }
            return blnoLocalCostExUpdateReqVos;
        } else {
            return Collections.emptyList();
        }
    }

}
